//
// File: socket.cpp
// Description: 
// Author: Leaf Corcoran
// Author: Adam Damiano
//

#include "socket.h"

bool Socket::winsockStarted = false;

Socket::Socket(SOCKET s, SOCKADDR_IN address) 
	: s(s), address(address)
{
	// ~
}

//
// 
bool Socket::startWinsock() {
	if (winsockStarted) return true;
	WSADATA wsaData;
	WORD version;
	int error;
	version = MAKEWORD(2,0);
	
	if (WSAStartup(version, &wsaData)) return false;
	
	if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 0) {
		closeWinsock();
		return false;
	}
	
	return true;
}

//
//
void Socket::closeWinsock() {
	WSACleanup();
}

//
//
void Socket::send(const void *ptr, int size) {
	::send(s, (char*)ptr, size, 0);
}

//
//
int Socket::recv(const void *ptr, int size) {
	int n  = ::recv(s, (char*)ptr, size, 0);
	return n;
}

//
//
void Socket::sendString(string str) {
	send(str.c_str(), str.length());
}


//
//
void Socket::close() {
	closesocket(s);
}

//
//
string Socket::toString() {
	ostringstream s;
	s << inet_ntoa(address.sin_addr) << ":" << ntohs(address.sin_port);
	return s.str();
}

//
//
Socket::~Socket() {
	closesocket(s);
}


//////////////////////////////
// ServerSocket
//////////////////////////////


//
//
ServerSocket::ServerSocket(int port) : port(port), backlog(10) {
	if (!winsockStarted) startWinsock();
	
	s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if (s == INVALID_SOCKET) {
		// Failure code here...
		printf("Error at socket(): %ld\n", WSAGetLastError());
		closeWinsock();
		exit(1);
	}
	
	memset(&address, 0, sizeof(address));
	address.sin_family = AF_INET;
	address.sin_port = htons(port);
	address.sin_addr.s_addr = INADDR_ANY;
	
	if (bind(s, (SOCKADDR*)&address, sizeof(address)) == SOCKET_ERROR) {
		// Failure code here...
		printf("Error at bind(): %ld\n", WSAGetLastError());
		closeWinsock();
		exit(1);
	}
	
	if (listen(s, backlog) == SOCKET_ERROR) {
		// Failure code here...
		printf("Error at listen(): %ld\n", WSAGetLastError());
		closeWinsock();
		exit(1);
	}
	
	printf("Listening on port %d ...\n", port);
}

//
//
Socket *ServerSocket::accept() {
	SOCKET ns;
	SOCKADDR_IN address;
	int size = sizeof(address);
	
	ns = ::accept(s, (SOCKADDR*)&address, &size);
	
	Socket *s = new Socket(ns, address);
	return s;
}

//////////////////////////////
// ClientSocket
//////////////////////////////


//
//
ClientSocket::ClientSocket(string & hostname, int port) {
	if (!winsockStarted) startWinsock();
	
	s = socket(AF_INET, SOCK_STREAM, 0);
	
	hostent *host;
	if ((host = gethostbyname(hostname.c_str())) == 0) {
		printf("Error at gethostname(): %ld\n", WSAGetLastError());
		closeWinsock();
		exit(1);
	}
	
	memset(&address, 0, sizeof(address));
	address.sin_family = AF_INET;
	address.sin_port = htons(port);
	address.sin_addr = *((in_addr*)host->h_addr);
	
	if (connect(s, (SOCKADDR*)&address, sizeof(address)) == SOCKET_ERROR)  {
		cerr << "Connect error: ";
		switch(WSAGetLastError()) {
			case WSAECONNREFUSED:
				cerr << "Connection refused." << endl;
				break;
			case WSAETIMEDOUT:
				cerr << "Connection timed out." << endl;
				break;
		}
		closeWinsock();
		exit(1);
	
	}
	
	
}

